/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 * 
 * Copyright (c) 2008-2009, The KiWi Project (http://www.kiwi-project.eu)
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions are met:
 * - Redistributions of source code must retain the above copyright notice, 
 *   this list of conditions and the following disclaimer.
 * - Redistributions in binary form must reproduce the above copyright notice, 
 *   this list of conditions and the following disclaimer in the documentation 
 *   and/or other materials provided with the distribution.
 * - Neither the name of the KiWi Project nor the names of its contributors 
 *   may be used to endorse or promote products derived from this software 
 *   without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
 * POSSIBILITY OF SUCH DAMAGE.
 * 
 * Contributor(s):
 * 
 * 
 */
package kiwi.core.model.user;

import kiwi.core.model.Constants;
import kiwi.core.model.annotations.RDF;
import kiwi.core.model.annotations.RDFType;
import kiwi.core.model.rdf.KiWiMediaContentLiteral;
import kiwi.core.model.rdf.KiWiNode;
import kiwi.core.model.rdf.KiWiResource;
import kiwi.core.model.rdf.KiWiUriResource;
import org.hibernate.annotations.*;
import org.hibernate.annotations.Cache;
import org.hibernate.validator.constraints.Email;

import javax.persistence.CascadeType;
import javax.persistence.*;
import javax.persistence.Entity;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * This entity represents the KiWiUser that is registered at the KiWi Platform.
 * He has a login name, a first and a last name, an email address, a creation date,
 * which is the date of his/her registration at the KiWi platform, a picture, a passwordHash and a
 * transient password, which is only stored temporarily.
 * He may have roles and be part of one or more than one groups and he also may have friends, 
 * which are other users, who are registered at the KiWi platform.
 * 
 * We are using JBoss Seams IdentityManagement for creating users, for login/logout actions 
 * and for permission handling.
 *
 *
 * @author Stephanie Stroka
 */
@Entity
@org.hibernate.annotations.Entity(optimisticLock=OptimisticLockType.VERSION)
@Table(uniqueConstraints = @UniqueConstraint(columnNames = "login"))
//@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
@RDFType(Constants.NS_KIWI_CORE + "KiWiUser")
@NamedQueries({
	@NamedQuery(name="currentUserFactory.getUserByLogin",
				query="from KiWiUser u " +
			    	  "where u.login = :login "),
	@NamedQuery(name="userService.getUserByName",
				query="from KiWiUser u " +
			    	  "inner join fetch u.resource " +
			    	  "where u.firstName = :fn and u.lastName = :ln"),
	@NamedQuery(name="userService.getUserByFacebookAcc",
				query="from KiWiUser u " +
			    	  "inner join fetch u.resource " +
			    	  "where u.fbId = :fbId"),
    @NamedQuery(name="userService.getUserByWebId",
                query="from KiWiUser u " +
                	  "inner join fetch u.resource " +
			    	  "where u.webId = :webId"),
    @NamedQuery(name="userService.getUserByUri",
				query="from KiWiUser u " +
			    	  "inner join fetch u.resource " +
			    	  "where u.resource.uri = :uri"),
    @NamedQuery(name="userService.getUserByResource",
                query="from KiWiUser u " +
                      "inner join fetch u.resource " +
                      "where u.resource = :resource"),
	@NamedQuery(name="userService.getUserByEmail",
				query="from KiWiUser u " +
					  "inner join fetch u.resource " +
	  				  "where u.email = :email"),
	@NamedQuery(name="currentUserFactory.getUserById",
		        query="from KiWiUser u " +
        	      "where u.id = :id "),	  				  
	@NamedQuery(name="kiwiEntityManager.queryUser",
				query="from KiWiUser o " +
			    	  "inner join fetch o.resource " +
			    	  "where o.resource.uri    IN (:uris)")
})
public class KiWiUser implements PermissionOwner, Serializable {
 
	private static final long serialVersionUID = 1L;


    /**
     * The login name of the user; must be unique and not null, indexed; mapped to the foaf:nick RDF property.
     */
	@Index(name="user_login_idx")
    @Column(unique=true, nullable=false)
    @RDF(Constants.NS_FOAF + "nick")
    @NotNull
    private String login;

    /**
     * The password of the user; currently stored in plain text
     */
	private String passwordHash;


    /**
     * The first name of the user; mapped to the foaf:firstName RDF property
     */
    @RDF(Constants.NS_FOAF + "firstName")
    private String firstName;

    /**
     * The last name of the user; mapped to all matching FOAF RDF properties
     */
    @RDF(value = {Constants.NS_FOAF + "name", 
    		Constants.NS_FOAF + "family_name", 
    		Constants.NS_FOAF + "familyName", 
    		Constants.NS_FOAF + "surname", 
    		Constants.NS_FOAF + "lastName"})
    private String lastName;

    /**
     * The email address of the user; mapped to the foaf:mbox RDF property
     */
    @Email
    @RDF(Constants.NS_FOAF + "mbox")
    private String email;

    /** the creation date of the KiWiUser **/
    @Temporal(value = TemporalType.TIMESTAMP)
    @Column(updatable = false, nullable=false)
    private Date created;


    /**
     * The set of roles a user has; roles are used in permission management to determine the kind of access of the user
     */
    @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH}, fetch=FetchType.LAZY)
    private Set<Role> roles;

    @ManyToMany(fetch=FetchType.LAZY)
    private List<KiWiGroup> groups;


    /**
     * Boolean flag to verify whether the user is allowed to log in
     */
//	@UserEnabled
    private boolean enabled;


    /** the content items this KiWiUser has authored **/
    // we do not cascade due to performance issues ...
    @OneToMany(fetch = FetchType.LAZY, mappedBy= "creator")
    @Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
    @BatchSize(size=20)
    private Set<KiWiNode> authoredContent;
    
    /** the content items currently watched by the user */
    @OneToMany(fetch = FetchType.EAGER)
    @JoinTable(name = "KIWIUSER_WATCHES",
	        joinColumns = @JoinColumn(name="user_id"),
	        inverseJoinColumns = @JoinColumn(name="contentitem_id")
	)
    @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
    private Set<KiWiResource> watchedContent;
    


    @RDF(Constants.NS_KIWI_CORE + "hasProfilePhoto")
    @OneToOne
    private KiWiMediaContentLiteral profilePhoto;

/*    @NotNull UPDATE: the NotNull annotation cannot be set anymore, 
 * 				because the very first user that will be created cannot 
 * 				get a KiWiresource. (The workflow would be: 
 * 				(1) Create user, 
 * 				(2) create URIResource before user gets persisted, 
 * 				(3) get into KiWiSynchronizationImpl, because the creation of a resource 
 * 					triggers the registration of the KiWiSyncronizationImpl,
 * 				(4) The KiWiSynchronizationImpl tries to create another user, because the 
 * 					Revision object needs it.)
 */
    @OneToOne(fetch=FetchType.EAGER)
    @Fetch(FetchMode.JOIN)
    private KiWiUriResource resource;
    

    @Column(unique=true)
    private String fbId;
    
    @Column(unique=true)
    private String webId;
    
    @NotNull    
    private boolean deleted = false;

    @Id
	@GeneratedValue(strategy=GenerationType.AUTO)
	private Long id;
    
    // optimistic locking; improve performance of merge etc.
    @Version
    private Long version;
    
    public KiWiUser() {
        created = new Date();
        enabled = true;
    	watchedContent = new HashSet<KiWiResource>();
	}

    public KiWiUser(String login) {
    	this();
    	this.login = login;
    }
    
    /**
     * returns the time of creation
     * @return
     */
    public Date getCreated() {
        return created;
    }

    /**
     * sets the time of creation
     * @param created
     */
    public void setCreated(Date created) {
        this.created = created;
    }

    /**
     * @return login
     */
    public String getLogin() {
        return login;
    }
    
    /**
     * Sets the login of the user
     * @param login
     */
    public void setLogin(String login) {
        this.login = login;
    }


    /**
	 * @param enabled the enabled to set
	 */
	public void setEnabled(boolean enabled) {
		this.enabled = enabled;
	}

	/**
	 * @return the enabled
	 */
	public boolean isEnabled() {
		return enabled;
	}


	public void setPasswordHash(String passwordHash) {
		this.passwordHash = passwordHash;
	}

	public String getPasswordHash() {
		return passwordHash;
	}

	/**
     * @return firstName
     */
    public String getFirstName() {
        return firstName;
    }
    
    /**
     * Sets the firstName
     * @param firstName
     */

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    /**
     * @return lastname
     */
    public String getLastName() {
        return lastName;
    }

    /**
     * Sets the lastname
     * @param lastname
     */
    public void setLastName(String lastname) {
        this.lastName = lastname;
    }

    /**
     * @return email
     */
    public String getEmail() {
        return email;
    }

    /**
     * Sets the email
     * @param email
     */
    public void setEmail(String email) {
        this.email = email;
    }


    /**
     * Return the content objects this KiWiUser has authored
     * @return
     */
    public Set<KiWiNode> getAuthoredContent() {
        return authoredContent;
    }

    /**
     * Set the content objects this KiWiUser has authored (used by JPA)
     * @param authoredContent
     */
    public void setAuthoredContent(Set<KiWiNode> authoredContent) {
        this.authoredContent = authoredContent;
    }

    
    
    /**
     * Get the set of content items on the user's watchlist.
     * @return
     */
    public Set<KiWiResource> getWatchedContent() {
		return watchedContent;
	}

    /**
     * Set the set of content items on the user's watchlist.
     * @param watchedContent
     */
	public void setWatchedContent(Set<KiWiResource> watchedContent) {
		this.watchedContent = watchedContent;
	}


    /* (non-Javadoc)
	 * @see kiwi.model.kbase.KiWiEntity#getResource()
	 */
	public KiWiUriResource getResource() {
		return resource;
	}

	/* (non-Javadoc)
	 * @see kiwi.model.kbase.KiWiEntity#setResource(kiwi.model.kbase.KiWiSpecialResource)
	 */
	public void setResource(KiWiUriResource resource) {
		this.resource = resource;
	}

	/* (non-Javadoc)
	 * @see java.lang.Object#hashCode()
	 */
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((login == null) ? 0 : login.hashCode());
		return result;
	}

	/* (non-Javadoc)
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		KiWiUser other = (KiWiUser) obj;
		if (getLogin() == null) {
			if (other.getLogin() != null)
				return false;
		} else if (!getLogin().equals(other.getLogin()))
			return false;
		return true;
	}

	/**
	 * @return the deleted
	 */
	public boolean isDeleted() {
		return deleted;
	}

	/**
	 * @param deleted the deleted to set
	 */
	public void setDeleted(boolean deleted) {
		this.deleted = deleted;
	}

	/**
	 * Return the profile photo content item object associated with this user.
	 */
	public KiWiMediaContentLiteral getProfilePhoto() {
		return profilePhoto;
	}
	
	public void setProfilePhoto(KiWiMediaContentLiteral profilePhoto) {
		this.profilePhoto = profilePhoto;
	}

	/**
	 * @param fbId the fbId to set
	 */
	public void setFbId(String fbId) {
		this.fbId = fbId;
	}

	/**
	 * @return the fbId
	 */
	public String getFbId() {
		return fbId;
	}

	/**
	 * @param webId the webId to set
	 */
	public void setWebId(String webId) {
		this.webId = webId;
	}

	/**
	 * @return the webId
	 */
	public String getWebId() {
		return webId;
	}

	public Long getVersion() {
		return version;
	}    

    /**
     * @return group
     */
    public List<KiWiGroup> getGroups() {
        return groups;
    }

    /**
     * Sets the group
     * @param group
     */
    public void setGroups(List<KiWiGroup> group) {
        this.groups = group;
    }
    
    /**
	 * @return the role
	 */
	public Set<Role> getRoles() {
		return roles;
	}

	/**
	 * @param roles the role to set
	 */
	public void setRoles(Set<Role> roles) {
		this.roles = roles;
	}

 	/**
	 * @param version the version to set
	 */
	public void setVersion(Long version) {
		this.version = version;
	}

	@Override
	public Long getId() {
		return id;
	}

	@Override
	public void setId(Long id) {
		this.id = id;
	}

	public String toString() {
		return "KiWiUser:"+login;
	}	
	
}
